package com.xdb.sqlparser.parser;

import com.xdb.sqlparser.exception.ParserException;

public class SQLParser {

	protected final SQLLexer lexer;

	protected String dbType;

	public SQLParser(String sql, String dbType) {
		this(new SQLLexer(sql), dbType);
		this.lexer.nextToken();
	}

	public SQLParser(String sql) {
		this(sql, null);
	}

	public SQLParser(SQLLexer lexer) {
		this(lexer, null);
	}

	public SQLParser(SQLLexer lexer, String dbType) {
		this.lexer = lexer;
		this.dbType = dbType;
	}

	public final SQLLexer getLexer() {
		return lexer;
	}

	public String getDbType() {
		return dbType;
	}

	protected boolean identifierEquals(String text) {
		return lexer.token() == SQLToken.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(text);
	}

	protected void acceptIdentifier(String text) {
		if (identifierEquals(text)) {
			lexer.nextToken();
		} else {
			setErrorEndPos(lexer.pos());
			throw new ParserException("syntax error, expect " + text + ", actual " + lexer.token());
		}
	}

	protected String as() {
		String alias = null;

		if (lexer.token() == SQLToken.AS) {
			lexer.nextToken();

			if (lexer.token() == SQLToken.LITERAL_ALIAS) {
				alias = '"' + lexer.stringVal() + '"';
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.IDENTIFIER) {
				alias = lexer.stringVal();
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.LITERAL_CHARS) {
				alias = "'" + lexer.stringVal() + "'";
				lexer.nextToken();
			} else {
				switch (lexer.token()) {
					case KEY :
					case INDEX :
					case CASE :
					case MODEL :
					case PCTFREE :
					case INITRANS :
					case MAXTRANS :
					case SEGMENT :
					case CREATION :
					case IMMEDIATE :
					case DEFERRED :
					case STORAGE :
					case NEXT :
					case MINEXTENTS :
					case MAXEXTENTS :
					case MAXSIZE :
					case PCTINCREASE :
					case FLASH_CACHE :
					case CELL_FLASH_CACHE :
					case KEEP :
					case NONE :
					case LOB :
					case STORE :
					case ROW :
					case CHUNK :
					case CACHE :
					case NOCACHE :
					case LOGGING :
					case NOCOMPRESS :
					case KEEP_DUPLICATES :
					case EXCEPTIONS :
					case PURGE :
					case INITIALLY :
					case END :
					case COMMENT :
					case ENABLE :
					case DISABLE :
					case SEQUENCE :
					case USER :
					case ANALYZE :
					case OPTIMIZE :
					case GRANT :
					case REVOKE :
					case FULL :
					case TO :
					case NEW :
					case INTERVAL :
					case LOCK :
					case LIMIT :
					case IDENTIFIED :
					case PASSWORD :
					case BINARY :
					case WINDOW :
					case OFFSET :
					case SHARE :
					case START :
					case CONNECT :
					case MATCHED :
					case ERRORS :
					case REJECT :
					case UNLIMITED :
					case BEGIN :
					case EXCLUSIVE :
					case MODE :
					case ADVISE :
						alias = lexer.stringVal();
						lexer.nextToken();
						return alias;
					case QUES :
						alias = "?";
						lexer.nextToken();
					default :
						break;
				}
			}

			if (alias != null) {
				while (lexer.token() == SQLToken.DOT) {
					lexer.nextToken();
					alias += ('.' + lexer.token().name());
					lexer.nextToken();
				}

				return alias;
			}

			if (lexer.token() == SQLToken.LPAREN) {
				return null;
			}

			throw new ParserException("Error : " + lexer.token());
		}

		if (lexer.token() == SQLToken.LITERAL_ALIAS) {
			alias = '"' + lexer.stringVal() + '"';
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.IDENTIFIER) {
			alias = lexer.stringVal();
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.LITERAL_CHARS) {
			alias = "'" + lexer.stringVal() + "'";
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.CASE) {
			alias = lexer.token.name();
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.USER) {
			alias = lexer.stringVal();
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.END) {
			alias = lexer.stringVal();
			lexer.nextToken();
		}

		switch (lexer.token()) {
			case KEY :
			case INTERVAL :
			case CONSTRAINT :
				alias = lexer.token().name();
				lexer.nextToken();
				return alias;
			default :
				break;
		}

		return alias;
	}

	protected void printError(SQLToken token) {
		String arround;
		if (lexer.mark >= 0 && (lexer.text.length() > lexer.mark + 30)) {
			if (lexer.mark - 5 > 0) {
				arround = lexer.text.substring(lexer.mark - 5, lexer.mark + 30);
			} else {
				arround = lexer.text.substring(lexer.mark, lexer.mark + 30);
			}

		} else if (lexer.mark >= 0) {
			if (lexer.mark - 5 > 0) {
				arround = lexer.text.substring(lexer.mark - 5);
			} else {
				arround = lexer.text.substring(lexer.mark);
			}
		} else {
			arround = lexer.text;
		}

		// throw new
		// ParserException("syntax error, error arround:'"+arround+"',expect "
		// + token + ", actual " + lexer.token() + " "
		// + lexer.stringVal() + ", pos " + this.lexer.pos());
		throw new ParserException("syntax error, error in :'" + arround + "',expect " + token + ", actual " + lexer.token() + " " + lexer.stringVal());
	}
	/**
	 * 如果输入的关键字是接受的，则读取下一个关键字；否则打印错误
	 * 
	 * @param token
	 *            关键字
	 */
	public void accept(SQLToken token) {
		if (lexer.token() == token) {
			lexer.nextToken();
		} else {
			setErrorEndPos(lexer.pos());
			printError(token);
		}
	}

	public void match(SQLToken token) {
		if (lexer.token() != token) {
			throw new ParserException("syntax error, expect " + token + ", actual " + lexer.token() + " " + lexer.stringVal());
		}
	}

	private int errorEndPos = -1;

	protected void setErrorEndPos(int errPos) {
		if (errPos > errorEndPos) {
			errorEndPos = errPos;
		}
	}

}
